package de.tuhh.simsearch.model.persistence.usermgmt.eao;

import java.util.LinkedList;
import java.util.List;

import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;

import org.eclipse.persistence.config.CacheUsage;
import org.eclipse.persistence.config.QueryHints;

import de.tuhh.simsearch.model.persistence.usermgmt.eao.interfaces.UserMgmtEAOInterface;
import de.tuhh.simsearch.model.persistence.usermgmt.entities.GroupEntity;
import de.tuhh.simsearch.model.persistence.usermgmt.entities.PreferenceEntity;
import de.tuhh.simsearch.model.persistence.usermgmt.entities.RoleEntity;
import de.tuhh.simsearch.model.persistence.usermgmt.entities.UserEntity;

/**
 * Session Bean implementation class UserMgmtEAO. The user management entity access object manages the access to the user database.
 * This database contains the users, groups, preferences and roles which are represented as entity objects. 
 * 
 * 
 * @author SimSearch 1
 * 
 * @see UserEntity
 * @see GroupEntity
 * @see RoleEntity
 * @see PreferenceEntity
 */
@Stateless
@LocalBean
public class UserMgmtEAO implements UserMgmtEAOInterface {

  /**
   * The entity manager handles the database access
   */
  @PersistenceContext private EntityManager em;

  /**
   * Default constructor.
   */
  public UserMgmtEAO() {

  }

  /**
   * Constructor for JUnit
   */
  public UserMgmtEAO(EntityManager em) {

    this.em = em;
  }

  @Override
  public boolean persistUser(UserEntity user) {

    boolean returnvar = true;

    if (!isUserNameFree(user.getName())) {
      returnvar = false;
    } else {

      try {
        em.persist(user);
      } catch (Exception e) {

        e.printStackTrace();
        returnvar = false;
      }
    }
    return returnvar;
  }

  @Override
  public UserEntity getUserEntity(String usrName) {

    List<UserEntity> myUsers = null;
    UserEntity myUser = null;

    TypedQuery<UserEntity> qUser = em.createQuery("SELECT u FROM UserEntity u WHERE u.name = :name", UserEntity.class)
        .setParameter("name", usrName);
    myUsers = qUser.getResultList();

    if (myUsers.size() == 1) {

      myUser = myUsers.get(0);
    }

    return myUser;
  }

  @Override
  public RoleEntity getRoleEntity(String name) {

    List<RoleEntity> entities = null;
    RoleEntity entity = null;

    TypedQuery<RoleEntity> qRole = em.createQuery("SELECT r FROM RoleEntity r WHERE r.roleName = :name",
        RoleEntity.class).setParameter("name", name);

    entities = qRole.getResultList();

    if (entities.size() == 1) {

      entity = entities.get(0);
    }

    return entity;
  }

  @Override
  public boolean isUserNameFree(String name) {

    TypedQuery<UserEntity> query = em.createQuery("SELECT u FROM UserEntity u WHERE u.name = :name", UserEntity.class)
        .setParameter("name", name);
    List<UserEntity> results = query.getResultList();

    return results.isEmpty();

  }

  @Override
  public boolean changePassword(String userName, String newpswd) {

    boolean rv = false;
    UserEntity myUser = null;

    TypedQuery<UserEntity> qUser = em.createQuery("SELECT u FROM UserEntity u WHERE u.name = :name", UserEntity.class)
        .setParameter("name", userName);
    List<UserEntity> userList = qUser.getResultList();

    if (userList.size() == 1) {
      myUser = userList.get(0);
      myUser.setPassword(newpswd);
      rv = true;
    }

    return rv;
  }

  @Override
  public boolean persistGroup(GroupEntity group) {

    boolean returnvar = true;

    if (!isGroupNameFree(group.getName())) {
      System.out.println("Group Name ist not free.");
      returnvar = false;
    } else {

      try {
        System.out.println("Trying to persist.");
        em.persist(group);

      } catch (Exception e) {
        
        System.out.println("Hmm exception.");
        e.printStackTrace();
        returnvar = false;
      }
    }

    return returnvar;
  }

  @Override
  public boolean deleteGroup(String GroupName) {

    boolean returnvar = true;

    try {

      GroupEntity myGroup = em.find(GroupEntity.class, GroupName);
      em.remove(myGroup);

    } catch (Exception e) {

      returnvar = false;
    }

    return returnvar;

  }

  @Override
  public GroupEntity getGroupEntity(String grpName) {

    GroupEntity myGroup = null;

    TypedQuery<GroupEntity> qGroup = em.createQuery("SELECT g FROM GroupEntity g WHERE g.name = :name",
        GroupEntity.class).setParameter("name", grpName);
    List<GroupEntity> grpList = qGroup.getResultList();

    if (grpList.size() == 1) {
      myGroup = grpList.get(0);
    }

    return myGroup;
  }

  @Override
  public List<String> searchInGroups(String srchString) {

    TypedQuery<String> qGroups = em.createQuery("SELECT g.name FROM GroupEntity g WHERE g.name LIKE :name",
        String.class).setParameter("name", "%" + srchString + "%");
    List<String> foundGroups = qGroups.getResultList();

    return foundGroups;
  }

  /*
   * one has to check if a name the user wants to give to a group is already used by another group
   */
  @Override
  public boolean isGroupNameFree(String name) {

    TypedQuery<GroupEntity> query = em.createQuery("SELECT g FROM GroupEntity g WHERE g.name = :name",
        GroupEntity.class).setParameter("name", name);
    List<GroupEntity> results = query.getResultList();

    return results.isEmpty();

  }

  @Override
  public boolean addUserToGroup(String userName, String groupName, boolean pending) {

    boolean rv = false;
    // find the user who wants to enter
    TypedQuery<UserEntity> qUser = em.createQuery("SELECT u FROM UserEntity u WHERE u.name = :name", UserEntity.class)
        .setParameter("name", userName);
    List<UserEntity> userList = qUser.getResultList();

    // find the group the user wants to enter
    TypedQuery<GroupEntity> qGroup = em.createQuery("SELECT g FROM GroupEntity g WHERE g.name = :name",
        GroupEntity.class).setParameter("name", groupName);
    List<GroupEntity> groupList = qGroup.getResultList();

    if (userList.size() == 1 && groupList.size() == 1) {

      if (pending == false) { // user is not a pending user

        groupList.get(0).addMember(userList.get(0));

      } else {

        // user is a pending user and so another set is used to insert
        groupList.get(0).addPendingUser(userList.get(0)); //
      }

      rv = true;

    } else {

      rv = false;
    }

    return rv; // true means only operation successful
  }

  @Override
  public boolean removeUserFromGroup(String userName, String groupName) {

    // To query all groups with a user whose name is userName - a join is
    // used.
    boolean returnvalue = false;

    TypedQuery<UserEntity> queryUser = em.createQuery("SELECT u FROM UserEntity u WHERE u.name = :userName",
        UserEntity.class).setParameter("userName", userName);
    List<UserEntity> userList = queryUser.getResultList();

    TypedQuery<GroupEntity> queryGroup = em.createQuery("SELECT g FROM GroupEntity g WHERE g.name = :groupName",
        GroupEntity.class).setParameter("groupName", groupName);
    List<GroupEntity> groupList = queryGroup.getResultList();

    if (groupList.size() == 1 && userList.size() == 1) {
      if (groupList.get(0).getMembers().contains(userList.get(0))) {
        groupList.get(0).removeMember(userList.get(0));
        returnvalue = true;
      } else if (groupList.get(0).getPendingUsers().contains(userList.get(0))) {
        groupList.get(0).removePendingUser(userList.get(0));
        returnvalue = true;
      }
    } 
    return returnvalue;
  }

  @Override
  public boolean isUserInGroup(String userName, String groupName) {

    boolean success = false;

    // To query all groups with a user whose name is userName - a join is
    // used.

    TypedQuery<GroupEntity> query = em.createQuery(
        "SELECT e FROM GroupEntity e JOIN e.members p WHERE p.name =:userName", GroupEntity.class);
    // find all groups where this user is subscribed
    List<GroupEntity> results = query.setParameter("userName", userName).getResultList();

    for (GroupEntity g : results) {
      if (g.getName() == groupName) {

        success = true;
      }

    }

    return success;

  }

  @Override
  public boolean deleteUser(String userName) {

    boolean returnval = true;

    UserEntity myUser = em.find(UserEntity.class, userName);

    try {
      em.remove(myUser);
    } catch (Exception e) {
      returnval = false;
    }
    return returnval;
  }

  @Override
  public boolean checkPassword(String usrName, String pswd) {

    boolean rv = false;
    TypedQuery<UserEntity> qUser = em.createQuery("SELECT u FROM UserEntity u WHERE u.name = :name", UserEntity.class)
        .setParameter("name", usrName);
    List<UserEntity> userList = qUser.getResultList();

    if (userList.size() == 1) {
      if (userList.get(0).getPassword() == pswd)
        rv = true;
    } else
      rv = false;

    return rv;

  }

  @Override
  public boolean addPreference(String userName, String categoryName, Integer contentId) {

    boolean rv = false;
    List<UserEntity> userList = null;
    List<PreferenceEntity> prefList = null;
    TypedQuery<UserEntity> qUser = null;
    TypedQuery<PreferenceEntity> qPreference = null;

    try {

      qUser = em.createQuery("SELECT u FROM UserEntity u WHERE u.name = :name", UserEntity.class).setParameter("name",
          userName);
      userList = qUser.getResultList();

    } catch (Exception e) {
      e.printStackTrace();
    }

    try {

      qPreference = em.createQuery(
          "SELECT p FROM PreferenceEntity p WHERE p.name = :name AND p.contentID = :contentId", PreferenceEntity.class)
          .setParameter("name", categoryName);
      qPreference.setParameter("contentId", contentId);
      prefList = qPreference.getResultList();
    } catch (Exception e) {
      e.printStackTrace();
    }

    try {
      if (userList.size() == 1 && prefList.size() == 1) {
        userList.get(0).addPreference(prefList.get(0)); // vorhandene preference suchen
        rv = true;
      }
    } catch (NullPointerException e) {
      e.printStackTrace();
      rv = false;
    }
    return rv;
  }

  @Override
  public boolean removePreference(String userName, String categoryName, Integer contentId) {

    boolean success = false;

    TypedQuery<UserEntity> qUser = em.createQuery("SELECT u FROM UserEntity u WHERE u.name = :name", UserEntity.class)
        .setParameter("name", userName);
    List<UserEntity> userList = qUser.getResultList();

    TypedQuery<PreferenceEntity> qPreference = em.createQuery(
        "SELECT p FROM PreferenceEntity p WHERE p.name = :name AND p.contentID = :contentId", PreferenceEntity.class)
        .setParameter("name", categoryName);
    qPreference.setParameter("contentId", contentId);
    List<PreferenceEntity> prefList = qPreference.getResultList();

    if (userList.size() == 1 && prefList.size() == 1) {
      userList.get(0).removePreference(prefList.get(0)); // vorhandene preference suchen
      success = true;
    }

    return success;
  }

  @Override
  public PreferenceEntity getPreferenceEntity(String categoryName, Integer contentId) {

    List<PreferenceEntity> plist = null;
    PreferenceEntity p = null;

    TypedQuery<PreferenceEntity> query = em
        .createQuery("SELECT p FROM PreferenceEntity p WHERE p.name = :name AND p.contentID = :contentId",
            PreferenceEntity.class).setParameter("name", categoryName).setParameter("contentId", contentId);
    plist = query.getResultList();

    if (plist.size() == 1) {

      p = plist.get(0);
    }

    return p;
  }

  @Override
  public boolean persistNewPreference(PreferenceEntity preference) {

    boolean returnvar = true;

    if (!isPreferenceNameFree(preference.getName(), preference.getContentID())) {

      returnvar = false;
    } else {

      try {
        em.persist(preference);

      } catch (Exception e) {

        e.printStackTrace();
        returnvar = false;
      }
    }

    return returnvar;
  }

  @Override
  public boolean isPreferenceNameFree(String name, Integer contentId) {

    boolean success = false;
    try {

      TypedQuery<PreferenceEntity> query = em.createQuery(
          "SELECT p FROM PreferenceEntity p WHERE p.name = :name AND p.contentID = :contentId", PreferenceEntity.class)
          .setParameter("name", name);
      query.setParameter("contentId", contentId);
      List<PreferenceEntity> results = query.getResultList();

      success = results.isEmpty();
    } catch (Exception e) {
      success = false;
    }

    return success;
  }

  @Override
  public List<String> searchInUsers(String srchstring) {

    TypedQuery<String> qUsers = em.createQuery("SELECT u.name FROM UserEntity u WHERE u.name LIKE :name", String.class)
        .setParameter("name", "%" + srchstring + "%");
    List<String> foundUsers = qUsers.getResultList();

    return foundUsers;
  }

  @Override
  public List<PreferenceEntity> searchInPreferences(String srchstring) {

    TypedQuery<PreferenceEntity> qPreferences = em.createQuery(
        "SELECT p FROM PreferenceEntity p WHERE p.name LIKE :name", PreferenceEntity.class).setParameter("name",
        srchstring + "%");
    List<PreferenceEntity> foundPrefrences = qPreferences.getResultList();

    return foundPrefrences;
  }

  @Override
  public List<String> searchUserPreferences(String userName) {

    List<String> usersPreferences = new LinkedList<String>();

    TypedQuery<PreferenceEntity> query = em
        .createQuery("SELECT u.preferences FROM UserEntity u WHERE u.name = :userName", PreferenceEntity.class)
        .setParameter("userName", userName).setHint(QueryHints.CACHE_USAGE, CacheUsage.DoNotCheckCache);

    List<PreferenceEntity> results = query.getResultList();

    for (PreferenceEntity c : results) {
      usersPreferences.add(c.getName());
    }

    if (usersPreferences.isEmpty()) {
      return null;
    } else
      return usersPreferences;
  }

  @Override
  public List<String> searchGroupMembers(String groupName) {

    List<String> groupMembers = new LinkedList<String>();

    TypedQuery<UserEntity> query = em
        .createQuery("SELECT g.members FROM GroupEntity g WHERE g.name = :groupName", UserEntity.class)
        .setParameter("groupName", groupName).setHint(QueryHints.CACHE_USAGE, CacheUsage.DoNotCheckCache);

    List<UserEntity> results = query.getResultList();

    for (UserEntity c : results) {
      groupMembers.add(c.getName());
    }

    if (groupMembers.isEmpty()) {
      return null;
    } else
      return groupMembers;
  }

  @Override
  public boolean isContentAdmin(String name) {
    
    TypedQuery<RoleEntity> query = em.createQuery("SELECT u.roles FROM UserEntity u WHERE u.name = :uname", RoleEntity.class).setParameter("uname", name);
    
    List<RoleEntity> result = query.getResultList();
    
    for (RoleEntity r: result) {
      if (r.getRoleName().equals("ADMIN")) {
        return true;
      }
    }
    
    return false;
  }
}